#
# Copyright (c) 2024 Project CHIP Authors
# Copyright 2024-2025 NXP
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

#
# @file
# CMake file that defines cmake functions common to NXP platforms
#

# *********************************************************************************
# Set common variables
# *********************************************************************************

if(NOT CHIP_ROOT)
    get_filename_component(CHIP_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../../.. REALPATH)
endif()

# ARMGCC_DIR shall be exported in order to build SDK examples with CMake (required for mcuboot build)
if(DEFINED ENV{ARMGCC_DIR})
    set(ARMGCC_DIR $ENV{ARMGCC_DIR})
else()
    if(DEFINED ENV{PW_ENVIRONMENT_ROOT})
        set(ARMGCC_DIR $ENV{PW_ENVIRONMENT_ROOT}/cipd/packages/arm)
    else()
        set(ARMGCC_DIR ${CHIP_ROOT}/.environment/cipd/packages/arm)
    endif()
endif()

message(STATUS "ARMGCC_DIR is set to : ${ARMGCC_DIR}")

find_package(Python3 REQUIRED)

# *********************************************************************************
# Retrieve NXP SDK path
# *********************************************************************************
# This function will retrieve where the platform SDK is located and export it
# in a dedicated variable
function(nxp_get_sdk_path platform sdk_path_variable)
    if(DEFINED ENV{${sdk_path_variable}})
        set(${sdk_path_variable} "$ENV{${sdk_path_variable}}" CACHE INTERNAL "NXP SDK root path")
    else()
        execute_process(
            COMMAND ${Python3_EXECUTABLE} -c "from nxp_matter_support.scripts.update_nxp_sdk import *; [print(p.sdk_storage_location_abspath) for p in ALL_PLATFORM_SDK if p.sdk_name == '${platform}'][0];"
            OUTPUT_VARIABLE OUTPUT_VAR
            OUTPUT_STRIP_TRAILING_WHITESPACE
            WORKING_DIRECTORY ${CHIP_ROOT}/third_party/nxp
        )
        set(${sdk_path_variable} "${OUTPUT_VAR}" CACHE INTERNAL "NXP SDK root path")
    endif()
endfunction()

# Set the NXP SDK path
set(NXP_SDK_ROOT ${SdkRootDirPath})

# Define output variable
set(APP_OUTPUT_DIR ${CMAKE_BINARY_DIR})
set(APP_EXECUTABLE_NAME app)
set(APP_EXECUTABLE_SUFFIX .elf)

# *********************************************************************************
# Build example application with GN and Ninja
# *********************************************************************************
function(nxp_build_app_with_gn)
    add_custom_command(
        OUTPUT ${gn_build}/build.ninja
        COMMAND export NXP_SDK_ROOT=${NXP_SDK_ROOT}
        COMMAND ${Python3_EXECUTABLE} ${CHIP_ROOT}/config/common/cmake/make_gn_args.py
        @${CMAKE_CURRENT_BINARY_DIR}/chip/args.tmp > ${gn_build}/args.gn
        COMMAND gn gen ${gn_build}
        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
    )

    add_custom_target(build_app_with_gn ALL
        COMMAND ninja -C ${gn_build}
        DEPENDS ${gn_build}/build.ninja
        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
        COMMENT "Building ${PROJECT_NAME} with GN and Ninja"
    )

    # Make sure the pre-build process is executed before building the application
    add_dependencies(build_app_with_gn pre_build)
endfunction(nxp_build_app_with_gn)

# *********************************************************************************
# Generate MCUBoot application
# *********************************************************************************
if(DEFINED CONFIG_CHIP_DEVICE_SOFTWARE_VERSION_STRING)
    string(REGEX REPLACE "\\\\" "" CONFIG_CHIP_DEVICE_SOFTWARE_VERSION_STRING ${CONFIG_CHIP_DEVICE_SOFTWARE_VERSION_STRING})
endif()

get_filename_component(MCUBOOT_OPENSOURCE_DIR "${NXP_SDK_ROOT}/middleware/mcuboot_opensource" REALPATH)

get_filename_component(MCUBOOT_EXAMPLE_DIR "${NXP_SDK_ROOT}/examples/ota_examples/mcuboot_opensource" REALPATH)

function(nxp_generate_mcuboot)
    if(DEFINED core_id)
        add_custom_target(build_mcuboot ALL
            COMMAND export ARMGCC_DIR=${ARMGCC_DIR}
            COMMAND west build -d ${CMAKE_CURRENT_BINARY_DIR}/mcuboot -b ${board} ${MCUBOOT_EXAMPLE_DIR} -Dcore_id=${core_id} -DCONF_FILE=${NXP_MATTER_SUPPORT_DIR}/cmake/${CONFIG_CHIP_NXP_PLATFORM_FOLDER_NAME}/bootloader.conf
            WORKING_DIRECTORY ${SdkRootDirPath}
            COMMENT "Generating MCUBoot binary"
        )
    else()
        add_custom_target(build_mcuboot ALL
            COMMAND export ARMGCC_DIR=${ARMGCC_DIR}
            COMMAND west build -d ${CMAKE_CURRENT_BINARY_DIR}/mcuboot -b ${board} ${MCUBOOT_EXAMPLE_DIR} -DCONF_FILE=${NXP_MATTER_SUPPORT_DIR}/cmake/${CONFIG_CHIP_NXP_PLATFORM_FOLDER_NAME}/bootloader.conf
            WORKING_DIRECTORY ${SdkRootDirPath}
            COMMENT "Generating MCUBoot binary"
        )
    endif()

    add_dependencies(build_mcuboot app)
endfunction(nxp_generate_mcuboot)

# *********************************************************************************
# Sign application binary with MCUBoot imgtool
# *********************************************************************************
function(nxp_sign_app_imgtool bin_sections_to_remove)
    string(REPLACE " " ";" bin_sections_to_remove ${bin_sections_to_remove})
    add_custom_command(
        OUTPUT ${APP_OUTPUT_DIR}/${APP_EXECUTABLE_NAME}.bin
        COMMAND arm-none-eabi-objcopy ${bin_sections_to_remove} -O binary ${APP_OUTPUT_DIR}/${APP_EXECUTABLE_NAME}${APP_EXECUTABLE_SUFFIX} ${APP_OUTPUT_DIR}/${APP_EXECUTABLE_NAME}.bin
        COMMENT "Converting the application .elf into binary"
    )

    # The mcuboot args "--pad --confirm" should only be used for images
    # which will be programmed directly into flash.
    # For OTA update images, the "--pad --confirm" should not be applied.
    # Here, we are assuming that images with SW version > 1 are update images.
    if(CONFIG_CHIP_DEVICE_SOFTWARE_VERSION LESS_EQUAL 1)
        set(CONFIG_MCUBOOT_ADDITIONAL_ARGS --pad --confirm)
    else()
        set(CONFIG_MCUBOOT_ADDITIONAL_ARGS "")
    endif()

    add_custom_target(sign_application ALL
        COMMAND ${Python3_EXECUTABLE} imgtool.py sign --key ${MCUBOOT_OPENSOURCE_DIR}/boot/nxp_mcux_sdk/keys/sign-rsa2048-priv.pem --align 4 --header-size ${CONFIG_CHIP_MCUBOOT_HEADER_SIZE} --pad-header
        --slot-size ${CONFIG_CHIP_MCUBOOT_SLOT_SIZE} --max-sectors ${CONFIG_CHIP_MCUBOOT_MAX_SECTORS} --version ${CONFIG_CHIP_DEVICE_SOFTWARE_VERSION_STRING} ${CONFIG_MCUBOOT_ADDITIONAL_ARGS} ${APP_OUTPUT_DIR}/${APP_EXECUTABLE_NAME}.bin ${APP_OUTPUT_DIR}/${APP_EXECUTABLE_NAME}_SIGNED.bin
        WORKING_DIRECTORY ${MCUBOOT_OPENSOURCE_DIR}/scripts
        DEPENDS ${APP_OUTPUT_DIR}/${APP_EXECUTABLE_NAME}.bin
        COMMENT "Sign the application binary with imgtool.py"
    )

    add_dependencies(sign_application app)

endfunction(nxp_sign_app_imgtool)

# *********************************************************************************
# Sign application binary with MCUBoot imgtool
# *********************************************************************************
set(EXTRA_OTA_ARGS "")

# We need to differentiate between the single-image .ota and the multi-image .ota generation,
# which use different scripts that require different arguments.
# Once all platforms can support the multi-image, the multi-image .ota generation will be kept as default.
if (CONFIG_NXP_GENERATE_MULTI_IMAGE_OTA)
    get_filename_component(CHIP_OTA_IMGTOOL_DIR "${CHIP_ROOT}/scripts/tools/nxp/ota" REALPATH)
    
    if(CONFIG_CHIP_OTA_ENCRYPTION)
        list(APPEND EXTRA_OTA_ARGS --enc_enable --input_ota_key ${CONFIG_CHIP_OTA_ENCRYPTION_KEY})
    endif()
    
    if(CONFIG_CHIP_OTA_FACTORY_DATA_PROCESSOR AND CONFIG_CHIP_FACTORY_DATA_BUILD AND CONFIG_CHIP_FACTORY_DATA_CERT_SOURCE_GENERATED)
        # convert decimal VID to its hexadecimal representation to find out certification files in repository
        math(EXPR LOCAL_VID "${CONFIG_CHIP_DEVICE_VENDOR_ID}" OUTPUT_FORMAT HEXADECIMAL)
        string(SUBSTRING ${LOCAL_VID} 2 -1 raw_vid)
        string(TOUPPER ${raw_vid} raw_vid_upper)

        # convert decimal PID to its hexadecimal representation to find out certification files in repository
        math(EXPR LOCAL_PID "${CONFIG_CHIP_DEVICE_PRODUCT_ID}" OUTPUT_FORMAT HEXADECIMAL)
        string(SUBSTRING ${LOCAL_PID} 2 -1 raw_pid)
        string(TOUPPER ${raw_pid} raw_pid_upper)
        
        list(APPEND EXTRA_OTA_ARGS --factory-data)
        list(APPEND EXTRA_OTA_ARGS --cert_declaration ${APP_OUTPUT_DIR}/factory_data/Chip-Test-CD-${raw_vid_upper}-${raw_pid_upper}.der)
        list(APPEND EXTRA_OTA_ARGS --dac_cert ${APP_OUTPUT_DIR}/factory_data/Chip-DAC-NXP-${raw_vid_upper}-${raw_pid_upper}-Cert.der)
        list(APPEND EXTRA_OTA_ARGS --dac_key ${APP_OUTPUT_DIR}/factory_data/Chip-DAC-NXP-${raw_vid_upper}-${raw_pid_upper}-Key.der)
        list(APPEND EXTRA_OTA_ARGS --pai_cert ${APP_OUTPUT_DIR}/factory_data/Chip-PAI-NXP-${raw_vid_upper}-${raw_pid_upper}-Cert.der)
    endif()
    
    # For a multi-image  "--app-input-file" must be specified to provide the application image
    list(APPEND EXTRA_OTA_ARGS --app-input-file ${APP_OUTPUT_DIR}/${APP_EXECUTABLE_NAME}_SIGNED.bin)
else()
    get_filename_component(CHIP_OTA_IMGTOOL_DIR "${CHIP_ROOT}/src/app" REALPATH)
    list(APPEND EXTRA_OTA_ARGS ${APP_OUTPUT_DIR}/${APP_EXECUTABLE_NAME}_SIGNED.bin)
endif()

function(nxp_generate_ota_file)
    add_custom_target(chip-ota-image ALL
        COMMAND ./ota_image_tool.py create -v ${CONFIG_CHIP_DEVICE_VENDOR_ID} -p ${CONFIG_CHIP_DEVICE_PRODUCT_ID} -vn ${CONFIG_CHIP_DEVICE_SOFTWARE_VERSION} -vs ${CONFIG_CHIP_DEVICE_SOFTWARE_VERSION_STRING} -da sha256 ${EXTRA_OTA_ARGS} ${APP_OUTPUT_DIR}/${APP_EXECUTABLE_NAME}.ota
        WORKING_DIRECTORY ${CHIP_OTA_IMGTOOL_DIR}
        COMMENT "Generating ota file"
    )

    add_dependencies(chip-ota-image sign_application)
endfunction(nxp_generate_ota_file)

# *********************************************************************************
# Pre-build process
# *********************************************************************************
function(nxp_pre_build_process)
    # Note : application build target should add dependency on the pre_build target
    # to ensure the pre-build process is executed before the application build
    add_custom_target(pre_build ALL
        COMMAND ${CMAKE_COMMAND} -E echo "Running pre-build process"
    )

    if(CONFIG_BUILD_K32W0_OT_RCP_IMAGE)
        # TODO : use k32w0 SDK cloned from nxp_matter_support repo instead,
        # once it is supported by the OT RCP build of k32w0.
        # nxp_get_sdk_path("k32w0" NXP_K32W0_SDK_ROOT)

        # Set up the k32w061 SDK inside ot-nxp repo
        add_custom_command(
            OUTPUT ${CHIP_ROOT}/third_party/openthread/ot-nxp/third_party/k32w061_sdk/.west
            COMMAND unset ZEPHYR_BASE && rm -rf .west && west init -l manifest --mf west.yml && west update
            WORKING_DIRECTORY ${CHIP_ROOT}/third_party/openthread/ot-nxp/third_party/k32w061_sdk/repo
        )

        # Build the k32w061 OT RCP image
        add_custom_target(build_k32w0_rcp ALL
            COMMAND ./script/build_k32w061 ot_rcp_ble_hci_bb_single_uart_fc
            DEPENDS ${CHIP_ROOT}/third_party/openthread/ot-nxp/third_party/k32w061_sdk/.west
            WORKING_DIRECTORY ${CHIP_ROOT}/third_party/openthread/ot-nxp
        )

        set(OT_RCP_K32W0_GENERATED_PATH "${CHIP_ROOT}/third_party/openthread/ot-nxp/build_k32w061/ot_rcp_ble_hci_bb_single_uart_fc/bin/ot-rcp-ble-hci-bb-k32w061.elf.bin.h" CACHE PATH "k32w0 rcp binary path")

        get_filename_component(OT_RCP_K32W0_GENERATED_PATH "${OT_RCP_K32W0_GENERATED_PATH}" ABSOLUTE)
        mcux_add_macro(
            K32W0_RCP_BINARY_H_FILE=\\\"${OT_RCP_K32W0_GENERATED_PATH}\\\"
        )

        # Make sure this is run as pre-build
        add_dependencies(pre_build build_k32w0_rcp)
    endif()
    
    # Print build information
    nxp_print_build_info()

    add_dependencies(${MCUX_SDK_PROJECT_NAME} pre_build)
endfunction(nxp_pre_build_process)

# *********************************************************************************
# Print build information
# *********************************************************************************
function(nxp_print_build_info)

# Get the transceiver name
set(TRANSCEIVER_NAME "none")
if(CONFIG_MCUX_COMPONENT_component.wifi_bt_module.IW416)
    set(TRANSCEIVER_NAME "IW416")
elseif(CONFIG_MCUX_COMPONENT_component.wifi_bt_module.IW61X)
    set(TRANSCEIVER_NAME "IW61X")
elseif(CONFIG_MCUX_COMPONENT_component.wifi_bt_module.88W8801)
    set(TRANSCEIVER_NAME "88W8801")
elseif(CONFIG_MCUX_COMPONENT_component.wifi_bt_module.K32W061_transceiver)
    set(TRANSCEIVER_NAME "K32W061")
endif()

#Get the connectivity mode
set(CONNECTIVITY_MODE "none")
if(CONFIG_CHIP_WIFI AND CONFIG_NET_L2_OPENTHREAD)
    set(CONNECTIVITY_MODE "WiFi + Thread")
elseif(CONFIG_CHIP_WIFI AND NOT CONFIG_NET_L2_OPENTHREAD)
    set(CONNECTIVITY_MODE "WiFi")
elseif(NOT CONFIG_CHIP_WIFI AND CONFIG_NET_L2_OPENTHREAD)
    set(CONNECTIVITY_MODE "Thread")
elseif(CONFIG_CHIP_ETHERNET)
    set(CONNECTIVITY_MODE "Ethernet")
endif()

# Print application build information
message(STATUS "")
message(STATUS "=======================================")
message(STATUS " Matter Application Build Info")
message(STATUS "---------------------------------------")
message(STATUS " Target Board : ${board}")
message(STATUS " Transceiver: ${TRANSCEIVER_NAME}")
message(STATUS " Connectivity Mode : ${CONNECTIVITY_MODE}") # e.g., WiFi / Thread / WiFi + Thread
message(STATUS " Build Type : ${CMAKE_BUILD_TYPE}") # e.g., Debug / Release
message(STATUS "=======================================")
message(STATUS "")

endfunction(nxp_print_build_info)
